﻿/*********************************************************
 * 开发人员：小兵
 * 创建时间：2012-10-9 13:54:06
 * 描述说明：
 * 
 * 更改历史：
 * 
 * *******************************************************/
namespace Mozlite.Data.Components.Installer
{
    using System;
    using System.Web;
    using System.Data;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;

    /// <summary>
    /// 安装类。
    /// </summary>
    public abstract class BaseInstaller<S> : BaseSqlHelper<S>, IInstaller where S : ISqlHelper
    {
        private readonly DatabaseVersion latestVersion;
        private DatabaseVersion? currentVersion = null;
        private static readonly Regex commentsRegex = new Regex(@"\/\*.*?\*\/", RegexOptions.Singleline | RegexOptions.Compiled);

        /// <summary>
        /// 获取当前版本。
        /// </summary>
        public DatabaseVersion CurrentVersion
        {
            get
            {
                if(!currentVersion.HasValue)
                    currentVersion = DetermineCurrentVersion();
                return currentVersion.Value;
            }
        }

        /// <summary>
        /// 获取最新版本。
        /// </summary>
        public DatabaseVersion LatestVersion
        {
            get { return latestVersion; }
        }

        /// <summary>
        /// 判断当前数据源是否可以连接。
        /// </summary>
        /// <value>
        /// 如果连接成功返回<c>true</c>，否则返回<c>false</c>。
        /// </value>
        public virtual bool CanConnect
        {
            get { return CurrentVersion != DatabaseVersion.Unavailable; }
        }

        /// <summary>
        /// 判断当前数据库是否为空数据库。
        /// </summary>
        /// <value>
        /// 如果为空数据库返回<c>true</c>，否则返回<c>false</c>。
        /// </value>
        public virtual bool IsEmpty
        {
            get { return CurrentVersion == DatabaseVersion.None; }
        }

        /// <summary>
        /// 获取当前数据库版本是否为最新版本。
        /// </summary>
        /// <value>
        /// 如果为最新版本返回<c>true</c>，否则返回<c>false</c>。
        /// </value>
        public bool IsLatestVersion
        {
            get { return CurrentVersion == LatestVersion; }
        }

        /// <summary>
        /// 判断当前数据库是否为可更新数据库。
        /// </summary>
        /// <value>
        /// 如果可以更新数据库返回<c>true</c>，否则返回<c>false</c>。
        /// </value>
        /// <remarks>空数据库不能更新，只能安装。</remarks>
        public virtual bool CanUpgrade
        {
            get { return false; }
        }

        /// <summary>
        /// 获取确认当前数据库版本唯一识别语句数组。
        /// </summary>
        protected abstract VersionSpecification[] VersionSpecs { get; }

        /// <summary>
        /// 初始化类<see cref="DefaultInstallerUtility&lt;S&gt;"/>。
        /// </summary>
        /// <param name="client">数据库辅助接口实例对象。</param>
        public BaseInstaller(S client)
            : this(client, DatabaseVersion.Unavailable)
        { }

        /// <summary>
        /// 初始化类<see cref="DefaultInstallerUtility&lt;S&gt;"/>。
        /// </summary>
        /// <param name="client">数据库辅助接口实例对象。</param>
        /// <param name="latestVersion">最新版本。</param>
        public BaseInstaller(S client, DatabaseVersion latestVersion)
            : base(client)
        {
            this.latestVersion = latestVersion;
        }
        /// <summary>
        /// 获取完全安装的SQL语句。
        /// </summary>
        protected abstract string FullInstallSql { get; }
        /// <summary>
        /// 更新使用的SQL语句。
        /// </summary>
        protected abstract string UpgradeSql { get; }

        /// <summary>
        /// 安装最新的数据库到目标源中。
        /// </summary>
        /// <exception cref="System.NotSupportedException">
        /// 如果不能安装或更新将抛出错误！
        /// </exception>
        public void Install()
        {
            if(IsLatestVersion)
                return;

            // 安装全新数据库
            if(IsEmpty)
            {
                NewInstall(FullInstallSql);
            }
            else
            // 旧版本更新数据库
            {
                if(!CanUpgrade)
                    throw new NotSupportedException("Upgrading from this version is not supported.");

                // 更新数据库
                Upgrade(UpgradeSql);
            }
        }
        /// <summary>
        /// 执行新安装方法。
        /// </summary>
        /// <param name="sql">SQL语句。</param>
        protected virtual void NewInstall(string sql)
        {
            ExecuteStatements(sql);
        }

        /// <summary>
        /// 执行更新方法。
        /// </summary>
        /// <param name="sql">SQL语句。</param>
        protected virtual void Upgrade(string sql)
        {
            ExecuteStatements(sql);
        }

        /// <summary>
        /// 确认数据库是否为当前版本。
        /// </summary>
        /// <returns>返回当前数据库版本。</returns>
        /// <remarks>在设置每个版本时，都需要有特殊的改变的才可以进行确认。</remarks>
        protected virtual DatabaseVersion DetermineCurrentVersion()
        {
            foreach(VersionSpecification v in VersionSpecs)
            {
                try
                {
                    if(v.ExpectedRows > -1)
                    {
                        using(var reader = SqlHelper.ExecuteReader(v.Sql))
                        {
                            var rowCount = 0;

                            while(reader.Read())
                                rowCount++;
                            //判断执行的结果和期望是否一样
                            if(v.ExpectedRows != rowCount)
                                continue;
                        }
                    }
                    else
                    {
                        SqlHelper.ExecuteNonQuery(v.Sql);
                    }

                    return v.Version;
                }
                catch { }
            }

            return DatabaseVersion.Unavailable;
        }

        /// <summary>
        /// 执行由封号分割的一系列的SQL语句。
        /// </summary>
        /// <param name="statements">SQL语句。</param>
        protected void ExecuteStatements(string statements)
        {
            if(string.IsNullOrEmpty(statements))
            {
                throw new ArgumentNullException("statements", "The sql statement to execute is empty. Database version: " + CurrentVersion.ToString());
            }

            // 将注释替换为空
            statements = commentsRegex.Replace(statements, " ");
            // 执行所有语句
            foreach(string statement in statements.Split(";".ToCharArray()))
            {
                string rawStatement = statement.Trim();
                if(rawStatement.Length > 0)
                    SqlHelper.ExecuteNonQuery(rawStatement);
            }
        }
    }
}
